package jp.tkym.labs.proc;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * Worker Thread of Java Prcess Launcher.
 * @author takayama
 */
public class JavaProcessWorker{
	private static final ExecutorService processExecutor = Executors.newFixedThreadPool(100);
	private final Process process;
	private List<Thread> ovservers = new ArrayList<Thread>();
	JavaProcessWorker(Process process){
		this.process = process;
		ovservers.add(new Thread(observe(process.getInputStream()), "observer_input_thread")); 
		ovservers.add(new Thread(observe(process.getErrorStream()), "observer_error_thread")); 
	}
	
	private void setup(){
		for(Thread t : ovservers) t.start();
	}
	
	private void teardown() throws InterruptedException{
		for(Thread t : ovservers) t.join();
	}
	
	Callable<Integer> call(){
		return new Callable<Integer>() {
			@Override
			public Integer call() throws Exception {
				setup();
				int exitCode = process.waitFor();
				teardown();
				return exitCode;
			}
		};
	}
	
	public interface JAsyncCallback<T>{
		public void onSuccess(T result);
		public void onFailure(Throwable t);
	}
	
	Runnable run(final JAsyncCallback<Integer> callback){
		return new Runnable() {
			@Override
			public void run() {
				try {
					setup();
					int exitCode = process.waitFor();
					teardown();
					callback.onSuccess(exitCode);
				} catch (Exception e) {
					callback.onFailure(e);
				}
			}
		};
	}
	
	public void async(JAsyncCallback<Integer> callback){
		processExecutor.submit(run(callback));
	}
	
	public Future<Integer> submit() {
		return processExecutor.submit(call());
	}
	
	public int execute() throws InterruptedException, ExecutionException{
		return processExecutor.submit(call()).get();
	}
	
	Process getProcess(){
		return process;
	}
	
	Runnable observe(final InputStream is){
		return new Runnable() {
			@Override
			public void run() {
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(is));
				while(true){
					try {
						String line = reader.readLine();
						if(line == null) break;
						System.out.println(line);
					} catch (IOException e) {
						try {
							reader.close();
						} catch (IOException e1) {
							e1.printStackTrace();
						}
					}
				}
			}
		};
	}
}